package com.example.myapplication.nestedscroll;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.RecyclerView;

import com.example.myapplication.nestedscroll.viewpager.InnerRecyclerView;

/**
 * Created by ymz0427 on 2021/8/21
 *
 */
public class CeilingScrollView extends NestedScrollView {

    private View topView;
    private View ceilingView;
    private View bottomView;

    //记录滑动速度
    private int velocityY;
    private int endScrollY = 0;

    private boolean isStartFling = false;

    private FlingHelper flingHelper;

    private int totalDy = 0;

    private final OnTopListenerImpl onTopListener = new OnTopListenerImpl();


    public CeilingScrollView(@NonNull Context context) {
        super(context);
        init();
    }

    public CeilingScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CeilingScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        flingHelper = new FlingHelper(getContext());
        setOnScrollChangeListener(new OnScrollChangeListener() {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                if (isStartFling) {
                    totalDy = 0;
                    isStartFling = false;
                }


                if (getScrollY() == topView.getMeasuredHeight()) {
                    dispatchFling();
                }
                notifyOnTop(getScrollY() == topView.getMeasuredHeight());

                totalDy += scrollY - oldScrollY;

                endScrollY = scrollY;
            }
        });
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        ViewGroup child = (ViewGroup) getChildAt(0);
        topView = child.getChildAt(0);
        ceilingView = child.getChildAt(1);
        bottomView = child.getChildAt(2);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup.LayoutParams layoutParams = bottomView.getLayoutParams();
        // 使ceilingView和bottomView的高度之和等于 整个 ItemCeilingScrollView的高度，于是ceilingView可以置顶
        layoutParams.height = getMeasuredHeight() - ceilingView.getMeasuredHeight();
    }


    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        //这个方法是由子View（实现了NestedScrollingChild接口)调用的，
        // 整个view向上滑（dy > 0) 且滑动距离 小于 topView的高度， 说明view仍然可以向上滑
        boolean topVisible = dy > 0 && getScrollY() < topView.getMeasuredHeight();
        if (topVisible) {
            scrollBy(0, dy);
            //告诉子View， 这个滑动距离我自己消费了
            consumed[1] = dy;
        }
    }

    @Override
    public void fling(int velocityY) {
        super.fling(velocityY);
        if (velocityY > 0) {
            //向上滑动才需要把自身剩余的滑动速度交给子View
            this.velocityY = velocityY;
            isStartFling = true;
        } else {
            this.velocityY = 0;
        }
    }

    private void dispatchFling() {
        //速度velocityY情况下能滑动的距离
        double splineFlingDistance = flingHelper.getSplineFlingDistance(velocityY);
        if (splineFlingDistance > totalDy) {

            //剩余距离转化成速度，交给子View滑动
            int childVelocityY = flingHelper.getVelocityByDistance(splineFlingDistance - totalDy);
            RecyclerView recyclerView = getRecyclerView(bottomView);
            if (recyclerView != null) {
                recyclerView.fling(0, childVelocityY);
            }
        }
        totalDy = 0;
        velocityY = 0;
        isStartFling = false;
    }

    private RecyclerView getRecyclerView(View container) {
        if (!(container instanceof ViewGroup)) {
            return null;
        }
        ViewGroup viewGroup = (ViewGroup) container;

        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View view = viewGroup.getChildAt(i);
            if (view instanceof RecyclerView && view.getClass() == InnerRecyclerView.class) {
                return (RecyclerView) view;
            } else if (view instanceof ViewGroup) {
                RecyclerView child = getRecyclerView((ViewGroup) view);
                if (child != null) {
                    return child;
                }
            }
        }
        return null;
    }

    private int startX;
    private int startY;


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (!onTopListener.flag) {
            return super.dispatchTouchEvent(ev);
        } else {
            int action = ev.getAction();
            int x = (int) ev.getX();
            int y = (int) ev.getY();
            if (action == MotionEvent.ACTION_DOWN) {
                if (!inside(ceilingView, x, y)) {
                    //不在ceilingView范围内， 交由super.onTouchEvent
                    startX = 0;
                    startY = 0;
                    return super.dispatchTouchEvent(ev);
                } else {
                    startX = x;
                    startY = y;
                }
            }
            return super.dispatchTouchEvent(ev);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!onTopListener.flag) {
            return super.onTouchEvent(ev);
        } else {
            int action = ev.getAction();
            if (action == MotionEvent.ACTION_MOVE
               || action == MotionEvent.ACTION_UP) {
                // ceilingView置顶后， 点按ceilingView区域， 不滑动区域
                if (startX != 0 && startY != 0) {
                    return false;
                }
            }

            return super.onTouchEvent(ev);
        }
    }

    private boolean inside(View view, float x, float y) {
        Rect rect = new Rect();
        view.getDrawingRect(rect);
        boolean in = rect.contains((int) x, (int) y);
        return in;
    }

    //----------------------滑动置顶监听-------------------------------------------

    public void setOnTopListener(boolean onTop, OnTopListener onTopListener) {
        this.onTopListener.setListener(onTopListener, onTop);
    }

    private void notifyOnTop(boolean onTop) {
        if (onTopListener != null) {
            onTopListener.onTop(onTop);
        }
    }

    private static class OnTopListenerImpl implements OnTopListener {

        private boolean flag;

        private OnTopListener listener;

        public OnTopListenerImpl() {

        }

        private void setListener(OnTopListener listener, boolean onTop) {
            this.listener = listener;
            this.flag = onTop;
        }

        @Override
        public void onTop(boolean flag) {
            //改变时才进行通知
            if (flag != this.flag) {
                this.flag = flag;
                if (listener != null) {
                    listener.onTop(flag);
                }
            }
        }
    }

    /**
     * 滑动置顶监听
     */
    public interface OnTopListener {
        void onTop(boolean flag);
    }
}
